﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Reflection;
using eTerm.NetSocket.PlugIn;
using System.IO;
using System.Text.RegularExpressions;

namespace eTerm.NetSocket
{

    #region 插件处理Handle
    /// <summary>
    /// 插件处理参数
    /// </summary>
    public sealed class PlugInArgs : EventArgs {
        /// <summary>
        /// Gets or sets the plug in.
        /// </summary>
        /// <value>The plug in.</value>
        public IPlugIn PlugIn { get; private set; }

        /// <summary>
        /// Gets or sets the description.
        /// </summary>
        /// <value>The description.</value>
        public string Description { get; private set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="PlugInArgs"/> class.
        /// </summary>
        /// <param name="plugIn">The plug in.</param>
        public PlugInArgs(IPlugIn plugIn,string description) {
            PlugIn = plugIn;
            Description = description;
        }

    }
    #endregion

    /// <summary>
    /// 配置池管理
    /// </summary>
    public sealed class eTermThreadPool
    {
        #region 变量定义
        private static readonly eTermThreadPool m_ThreadPool = new eTermThreadPool();
        private List<BaseTermSocket> m_eTermSocketPool = new List<BaseTermSocket>();
        private static object m_syncObj = new object();
        private int m_IG_INTERVAL = 60 * 5;
        private List<IPlugIn> m_plugIns = new List<IPlugIn>();
        private bool m_isRun = false;
        private bool m_AllowReadSecond = true;
        #endregion

        #region 事件定义
        /// <summary>
        /// Occurs when [on dequeue].
        /// </summary>
        public event EventHandler OnDequeue;

        /// <summary>
        /// Occurs when [on enueue].
        /// </summary>
        public event EventHandler OnEnqueue;

        /// <summary>
        /// Occurs when [on detect plug in].
        /// </summary>
        public event EventHandler<PlugInArgs> OnDetectPlugIn;

        /// <summary>
        /// 插件执行完成.
        /// </summary>
        public event EventHandler<PlugInArgs> OnPlugInExecuted;

        /// <summary>
        /// 插件执行异常.
        /// </summary>
        public event EventHandler<PlugInArgs> OnPlugInException;

        /// <summary>
        /// 处理查找代理
        /// </summary>
        internal delegate void PlugInCallback(string plugInPath);

        /// <summary>
        /// 插件搜索完毕.
        /// </summary>
        public event EventHandler OnDetectCompleted;

        public event EventHandler<PlugInArgs> OnDetectException;
        #endregion

        #region queue
        /// <summary>
        /// 获取空闲配置.
        /// </summary>
        /// <returns></returns>
        public BaseTermSocket Dequeue(eTerm363Session TSession,out bool isNew)
        {
            lock (m_syncObj)
            {
                isNew = false;
                
                //TODO：读秒规则      2012-12-03
                BaseTermSocket eTermSession = m_eTermSocketPool
                    .OrderBy(t => t.SentCount)
                    .FirstOrDefault(t => 
                        t.IsValidated 
                        && 
                        (t.TSession == null
                        ||
                        t.TSession.SessionTimeInterval>=t.TSession.Timeout
                        )
                        && 
                        t.Groups.Count(g => g == TSession.GroupCode) > 0);

                #region 非读秒处理
                if (!m_AllowReadSecond && eTermSession ==null)
                {
                    #region   //TODO:非读秒   分配占用配置时间最长用户给新用户   2012-12-03
                    //if (eTermSession == null){
                    //    eTermSession = m_eTermSocketPool
                    //                       .OrderBy(t => t.AssignDateTime)
                    //                       .FirstOrDefault(t =>
                    //                           t.IsValidated
                    //                           &&
                    //                           //(DateTime.Now.Subtract(t.AssignDateTime ?? DateTime.Now).TotalSeconds > 2000)
                    //                           //&&
                    //                           t.Groups.Count(g => g == TSession.GroupCode) > 0);
                    //}
                    #endregion

                    #region //TODO：读秒规则未获得配置就拿空闲时间最长的      2012-12-03
                    if (eTermSession == null)
                    {
                        eTermSession = m_eTermSocketPool
                                           .OrderBy(t => (t.TSession.Timeout - t.TSession.SessionTimeInterval))
                                           //.OrderBy(t => t.AssignDateTime)
                                           .FirstOrDefault(t =>
                                               t.IsValidated
                                               &&
                                               t.Groups.Count(g => g == TSession.GroupCode) > 0);
                    }
                }
                #endregion

                #endregion

                if (eTermSession == null) return null;
                if (eTermSession != null && eTermSession.TSession != null && eTermSession.TSession.ID != TSession.ID) {
                    eTermSession.TSession.eTermSession = null;
                    eTermSession.AssignDateTime = DateTime.Now;
                    eTermSession.TSession.SendDatagram(__eTermPakcet.BuildSessionPacket(eTermSession.TSession.RID, eTermSession.TSession.SID, @"注意:配置被切换."));
                    if(!TSession.IsApiSession)
                        eTermSession.SendStream(@"IG");
                    isNew = true;
                    if (OnDequeue != null)
                        OnDequeue(eTermSession, EventArgs.Empty);
                }
                TSession.eTermSession = eTermSession;
                TSession.eTermSession.TSession = TSession;
                return eTermSession;
            }
        }

        /// <summary>
        /// 重连资源.
        /// </summary>
        /// <param name="eTermSession">The e term session.</param>
        private void ReconnectermSession(BaseTermSocket eTermSession)
        {
            eTermSession.Connect();
        }

        /// <summary>
        /// 活动配置池.
        /// </summary>
        /// <value>The e term socket pool.</value>
        public List<BaseTermSocket> eTermSocketPool {
            get {
                lock (m_eTermSocketPool) {
                    List<BaseTermSocket> items = new List<BaseTermSocket>();
                    foreach (BaseTermSocket s in m_eTermSocketPool)
                        items.Add(s);
                    return items;
                }
            }
        }

        /// <summary>
        /// 是否存在于配置池中.
        /// </summary>
        /// <param name="Code">The code.</param>
        /// <returns></returns>
        public bool Exist(long Code) {
            return m_eTermSocketPool.Count(t => t.Code == Code) > 0;
        }

        /// <summary>
        /// Enqueues the specified e term session.
        /// </summary>
        /// <param name="eTermSession">The e term session.</param>
        public void Enqueue(BaseTermSocket eTermSession)
        {
            lock (m_syncObj)
            {
                if (eTermSession == null) return;
                m_eTermSocketPool.RemoveAll(t => t.Code == eTermSession.Code);
                m_eTermSocketPool.Add(eTermSession);
                if (OnEnqueue != null)
                    OnEnqueue(eTermSession, EventArgs.Empty);
            }
        }
        #endregion

        #region 属性定义
        /// <summary>
        /// Gets the instance.
        /// </summary>
        /// <value>The instance.</value>
        public static eTermThreadPool Instance { get { return m_ThreadPool; } }


        /// <summary>
        /// 守护线程检测间隔(单位：秒).
        /// </summary>
        /// <value>The I g_ INTERVAL.</value>
        public int IG_INTERVAL { set { m_IG_INTERVAL = value; } }

        /// <summary>
        /// 插件路径.
        /// </summary>
        /// <value>The plug in path.</value>
        public string PlugInPath { private get; set; }



        /// <summary>
        /// 后续插件列表缓存
        /// </summary>
        public Dictionary<string, string> AfterPlugIns { get; set; }

        /// <summary>
        /// 是否开启读秒功能
        /// </summary>
        public bool AllowReadSecond { set { m_AllowReadSecond = value; } }


        /// <summary>
        /// 插件列表.
        /// </summary>
        /// <value>The plug ins.</value>
        public List<IPlugIn> PlugIns { get { return m_plugIns; } }
        #endregion

        #region 开启守护线程
        /// <summary>
        /// 配置状态守护线程
        /// </summary>
        /// <returns></returns>
        public bool StartWatch() {
            if (!m_isRun)
                return true;
            else
                return false;
        }
        #endregion

        #region 构造函数
        /// <summary>
        /// Initializes a new instance of the <see cref="eTermThreadPool"/> class.
        /// </summary>
        private eTermThreadPool()
        {
            AfterPlugIns = new Dictionary<string, string>();
        }
        #endregion

        #region 配置管理
        /// <summary>
        /// 关闭所有配置.
        /// </summary>
        public void CloseAll() {
            for (int i = 0; i < m_eTermSocketPool.Count; i++) {
                m_eTermSocketPool[i].ReconnectCount = 0;
                m_eTermSocketPool[i].AllowReconnect = false;
                Close(m_eTermSocketPool[i].Code);
            }
        }

        /// <summary>
        /// 关闭指定配置.
        /// </summary>
        /// <param name="Id">The id.</param>
        private void Close(long Id)
        {
            lock (m_eTermSocketPool) {
                BaseTermSocket e = m_eTermSocketPool.FirstOrDefault(t => t.Code == Id);
                if (e != null)
                    e.Close();
            }
        }

        /// <summary>
        /// 初始化配置绑定事件.
        /// </summary>
        /// <param name="eTermSession">The e term session.</param>
        public void IniteTermSocket(BaseTermSocket eTermSession)
        {
            //if (m_eTermSocketPool.Count >= eTermSession.KeyManager.SetupBin.MaxSocket) return;
            eTermSession.AutoSendInterval = m_IG_INTERVAL;
            eTermSession.AllowReconnect = true;
            eTermSession.OnDisconnecting += new EventHandler(
                    delegate(object sender, EventArgs e)
                    {
                        lock (m_eTermSocketPool)
                        {
                            BaseTermSocket eterm = (sender as BaseTermSocket);
                            m_eTermSocketPool.RemoveAll(t => t.Code == eterm.Code);
                            if(eterm.TSession!=null && !eterm.TSession.IsApiSession)
                                eterm.TSession.SendDatagram(__eTermPakcet.BuildSessionPacket(eterm.TSession.RID, eterm.TSession.SID, @"配置异常断线,请尝试使用新配置!"));
                            //ReconnectermSession(sender as BaseTermSocket);
                        }
                    }
                );

            eTermSession.OnValidated += new EventHandler(
                    delegate(object sender, EventArgs e)
                    {
                        Enqueue(sender as BaseTermSocket);
                    }
                );
            eTermSession.OnReadPacket += new EventHandler<SocketPacketEventArgs>(
                    delegate(object sender, SocketPacketEventArgs e)
                    {
                        BaseTermSocket socket = sender as BaseTermSocket;
                        if (socket.TSession == null) return;

                        byte[] buffer = e.Buffer;
                        if (buffer[8] == socket.RID && buffer[9] == socket.SID)
                        {
                            buffer[8] = socket.TSession.RID;
                            buffer[9] = socket.TSession.SID;
                            string cmd = Encoding.GetEncoding(@"gb2312").GetString(eTermSession.Depacket(eTermSession.Outpacket.GetStream()));
                            string result = Encoding.GetEncoding(@"gb2312").GetString(eTermSession.Depacket(eTermSession.Inpacket.GetStream()));
                            AfterExecutePlugIn(cmd, result, e.eTermSession, e.eTermSession.TSession);
                        }
                        socket.TSession.SendDatagram(buffer);
                    }
                );
            eTermSession.Connect();
        }
        #endregion

        #region 插件处理
        /// <summary>
        /// 按指令搜索插件.
        /// </summary>
        /// <param name="CmdArray">The CMD array.</param>
        /// <returns></returns>
        public IPlugIn GetPlugIn(List<string> CmdArray) {
           IPlugIn plugIn=  this.m_plugIns.FirstOrDefault(p => CmdArray.Count(m => Regex.IsMatch(m.Trim()+@" ", p.PlugInCmd+@"\s+", RegexOptions.IgnoreCase | RegexOptions.Multiline)) > 0);
           return plugIn;
        }

        /// <summary>
        /// 执行插件代理
        /// </summary>
        delegate void AfterExecutePlugInCallback(string CmdString,string CmdResult, BaseTermSocket eTermSession, eTerm363Session TSession);

        /// <summary>
        /// Afters the execute plug in.
        /// </summary>
        /// <param name="CmdString">The CMD string.</param>
        /// <param name="CmdResult">The CMD result.</param>
        /// <param name="eTermSession">The e term session.</param>
        /// <param name="TSession">The T session.</param>
        private void AfterExecutePlugIn(string CmdString, string CmdResult, BaseTermSocket eTermSession, eTerm363Session TSession) {
            AfterExecutePlugInCallback m_execute = new AfterExecutePlugInCallback(ExecuteAfterPlugIn);
            m_execute.BeginInvoke(CmdString, CmdResult, eTermSession, TSession, new AsyncCallback(delegate(IAsyncResult iar) {
                AfterExecutePlugInCallback execute = iar.AsyncState as AfterExecutePlugInCallback;
                execute.EndInvoke(iar);
            }), m_execute);
        }

        /// <summary>
        /// 执行后续插件
        /// </summary>
        /// <param name="CmdArray"></param>
        private void ExecuteAfterPlugIn(string CmdString, string CmdResult, BaseTermSocket eTermSession, eTerm363Session TSession)
        {
            string[] CmdArray = CmdString.Split(new char[] {'\n','\r' }, StringSplitOptions.RemoveEmptyEntries);
            List< KeyValuePair<string, string>> plugInKeys = AfterPlugIns.Where(p => !string.IsNullOrEmpty(p.Key) && CmdArray.Count(m => !string.IsNullOrEmpty(m) && Regex.IsMatch(m.Trim(), @"^" + p.Key.Trim(), RegexOptions.IgnoreCase | RegexOptions.Multiline)) > 0).ToList();

            plugInKeys = plugInKeys.Distinct<KeyValuePair<string, string>>()
                .Distinct<KeyValuePair<string, string>>(new CommonEqualityComparer<KeyValuePair<string, string>,string>(t=>t.Value))
                .ToList();
            foreach (KeyValuePair<string, string> plugInKey in plugInKeys) {
                if (string.IsNullOrEmpty(plugInKey.Key)) continue;
                IPlugIn plugIn = this.m_plugIns.FirstOrDefault(p => Regex.IsMatch(plugInKey.Value.Trim() + @" ", p.PlugInCmd + @"\s+", RegexOptions.IgnoreCase | RegexOptions.Multiline));
                if (plugIn == null) return;
                try {
                    plugIn.Execute(eTermSession, TSession, CmdResult);
                }
                catch { }
            }
        }


        #region EqualityComparer
        internal class CommonEqualityComparer<T, V> : IEqualityComparer<T> {
            private Func<T, V> keySelector;
            private IEqualityComparer<V> comparer;

            public CommonEqualityComparer(Func<T, V> keySelector, IEqualityComparer<V> comparer) {
                this.keySelector = keySelector;
                this.comparer = comparer;
            }
            public CommonEqualityComparer(Func<T, V> keySelector)
                : this(keySelector, EqualityComparer<V>.Default) { }

            public bool Equals(T x, T y) {
                return comparer.Equals(keySelector(x), keySelector(y));
            }
            public int GetHashCode(T obj) {
                return comparer.GetHashCode(keySelector(obj));
            }
        }
        #endregion

        /// <summary>
        /// 在插件目录中搜索所有有效插件.
        /// </summary>
        public void DetectPlugIns() {
            m_plugIns = new List<IPlugIn>();
            PlugInCallback dectect = new PlugInCallback(LoadPlugIn);
            dectect.BeginInvoke(PlugInPath, new AsyncCallback(delegate(IAsyncResult iar) {
                PlugInCallback m_detect = iar.AsyncState as PlugInCallback;
                m_detect.EndInvoke(iar);
                iar.AsyncWaitHandle.Close();
                if (OnDetectCompleted != null)
                    OnDetectCompleted(this, EventArgs.Empty);

                //装载后续插件列表
            }), dectect);
        }

        /// <summary>
        /// Loads the plug in.
        /// </summary>
        /// <param name="plugInPath">The plug in path.</param>
        private void LoadPlugIn(string plugInPath) {
            if (string.IsNullOrEmpty(plugInPath))
                plugInPath = Path.GetDirectoryName(this.GetType().Assembly.Location);
            foreach (FileInfo f in new DirectoryInfo(plugInPath).GetFiles(@"*.PlugIn", SearchOption.AllDirectories)) {
                LoadPlugIn(f);
            }
        }

        /// <summary>
        /// Loads the plug in.
        /// </summary>
        /// <param name="File">The file.</param>
        public void LoadPlugIn(FileInfo File)
        {
            Assembly ass;
            try
            {
                if (!File.Name.StartsWith(@"eTerm.", StringComparison.CurrentCultureIgnoreCase)) return;
                ass = Assembly.LoadFrom(File.FullName);
                foreach (Type t in ass.GetTypes())
                {
                    foreach (Type i in t.GetInterfaces())
                    {
                        if (i.FullName == typeof(IPlugIn).FullName&&!t.IsAbstract)
                        {
                            IPlugIn plugIn = (IPlugIn)System.Activator.CreateInstance(t);
                            plugIn.OnExecuted += new EventHandler<PlugInArgs>(
                                    delegate(object sender, PlugInArgs e) {
                                        if (OnPlugInExecuted != null)
                                            OnPlugInExecuted(sender, e);
                                    }
                                );
                            plugIn.OnPlugInException += new EventHandler<PlugInArgs>(
                                    delegate(object sender, PlugInArgs e) {
                                        if (OnPlugInException != null)
                                            OnPlugInException(sender, e);
                                    }
                                );
                            IPlugIn innerPlugIn= m_plugIns.FirstOrDefault(p => p.PlugInCmd.ToLower().Trim() == plugIn.PlugInCmd.ToLower().Trim());
                            if (innerPlugIn != null)
                                m_plugIns.Remove(innerPlugIn);
                            m_plugIns.Add(plugIn);
                            if(OnDetectPlugIn!=null)
                                OnDetectPlugIn(plugIn,new PlugInArgs(plugIn,string.Empty));
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                //throw ex;
                if (this.OnDetectException != null)
                    OnDetectException(this,new PlugInArgs(null,ex.Message));
            }
        }
        #endregion
    }
}
